package org.springframework.samples.petclinic.util;

import java.io.IOException;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.util.Random;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.samples.petclinic.web.SleepController;

public abstract class SleepUtils {

	private static Logger logger = LoggerFactory.getLogger(SleepUtils.class);
	static final Lock lock = new ReentrantLock(true);
	
	public static void allSleep(long sleepTime) throws InterruptedException, IOException {
		logger.debug("start allSleep, sleepTime: " + sleepTime + " thread: " + Thread.currentThread().getId() + " " + Thread.currentThread().getName());
		threadSleep(sleepTime);
		waitSleep(sleepTime);
		activeLoopSleep(sleepTime);
		socketReadSleep(sleepTime);
		logger.debug("end allSleep");
	}

	private static int port = 3333;
	public static void socketReadSleep(long sleepTime) throws IOException {
		logger.debug("start socketReadSleep, sleepTime: " + sleepTime + " thread: " + Thread.currentThread().getId() + " " + Thread.currentThread().getName() + " port: " + port);
		ServerSocket socket = null;
		
		while (socket == null) {
			try {
				socket = new ServerSocket(port++);
			} catch (BindException e) {
				logger.info("port already used " + (port-1) + " "  + e);
			}
		}
		socket.setSoTimeout((int) sleepTime);
		Socket client = null;
		try {
			client = socket.accept();
		} catch (SocketTimeoutException e) {
			logger.debug("expected " + e);
		}
		logger.debug("clientSocket: " + client);
		logger.debug("end socketReadSleep");
	}
	
//	public static void systemInReadSleep(long sleepTime) throws IOException {
//		logger.debug("start systemInReadSleep, sleepTime: " + sleepTime);
//		System.in.r
//		logger.debug("end systemInReadSleep");
//	}

	public static void activeLoopSleep(long sleepTime) {
		logger.debug("start activeLoopSleep, sleepTime: " + sleepTime + " thread: " + Thread.currentThread().getId() + " " + Thread.currentThread().getName());
		long start = System.currentTimeMillis();
		
		Random random = new Random(System.currentTimeMillis());
		
		double tan = 0;
		
		while ((System.currentTimeMillis() - start) < sleepTime) {
			double  tg = Math.tan(random.nextDouble());
			tan = tg;
			
		}
		
		logger.debug("tan " + tan);
		logger.debug("end activeLoopSleep");
	}

	public static void waitSleep(long sleepTime) throws InterruptedException {
		logger.debug("start waitSleep, sleepTime: " + sleepTime + " thread: " + Thread.currentThread().getId() + " " + Thread.currentThread().getName());
		synchronized (SleepUtils.class) {
			logger.debug("acquired monitor, will call wait");
			SleepUtils.class.wait(sleepTime);
		}
		logger.debug("end waitSleep");
	}

	public static void threadSleep(long sleepTime) throws InterruptedException {
		logger.debug("start threadSleep, sleepTime: " + sleepTime + " thread: " + Thread.currentThread().getId() + " " + Thread.currentThread().getName());
		Thread.sleep(sleepTime);
		logger.debug("end threadSleep");
	}
	

    public static synchronized void intrinsicLock() {
    	logger.debug("start intrinsicLock " + " thread: " + Thread.currentThread().getId() + " " + Thread.currentThread().getName());
        Thread th = new Thread(new Runnable() {
            public void run() {
            	logger.debug("start intrinsicLock inner thread, before intrinsicLock call, " + " thread: " + Thread.currentThread().getId() + " " + Thread.currentThread().getName());
                intrinsicLock();
                logger.debug("end   intrinsicLock inner thread, after  intrinsicLock call, " + " thread: " + Thread.currentThread().getId() + " " + Thread.currentThread().getName());
            }
        }, "My intrinsicLock thread");
        th.start();
        try {
        	logger.debug("intrinsicLock before join " + " thread: " + Thread.currentThread().getId() + " " + Thread.currentThread().getName());
            th.join();
            logger.debug("intrinsicLock after  join " + " thread: " + Thread.currentThread().getId() + " " + Thread.currentThread().getName());
        } catch (InterruptedException e) {
        	logger.info("", e);
        }
        logger.debug("end intrinsicLock " + " thread: " + Thread.currentThread().getId() + " " + Thread.currentThread().getName());
    }

    public static void reentrantLock() {
       	logger.debug("start reentrantLock, before lock " + " thread: " + Thread.currentThread().getId() + " " + Thread.currentThread().getName());
        lock.lock();
        logger.debug("reentrantLock         after lock" + " thread: " + Thread.currentThread().getId() + " " + Thread.currentThread().getName());
        Thread th = new Thread(new Runnable() {
            public void run() {
                logger.debug("start reentrantLock inner thread, before reentrantLock call, " + " thread: " + Thread.currentThread().getId() + " " + Thread.currentThread().getName());
                reentrantLock();
                logger.debug("end   reentrantLock inner thread, after  reentrantLock call, " + " thread: " + Thread.currentThread().getId() + " " + Thread.currentThread().getName());
            }
        }, "My reentrantLock thread");
        th.start();
        try {
        	logger.debug("reentrantLock before join " + " thread: " + Thread.currentThread().getId() + " " + Thread.currentThread().getName());
            th.join();
            logger.debug("reentrantLock after  join " + " thread: " + Thread.currentThread().getId() + " " + Thread.currentThread().getName());
        } catch (InterruptedException e) {
        	logger.info("", e);
        }
        lock.unlock();
        logger.debug("end reentrantLock " + " thread: " + Thread.currentThread().getId() + " " + Thread.currentThread().getName());
    }

}
